package lsem.eclipse.vdf.ui.misc;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import lsem.eclipse.vdf.ui.Activator;

import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.ReadableIndex;
import org.neo4j.kernel.Config;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.osgi.framework.Bundle;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class DbGraphCreator {

	private Node rootNode;
	private GraphDatabaseService graphDb;
	private String PATHTODB;
	private File projectFile;

	public DbGraphCreator(String projectName) {
		this.prepareDBConfiguration(projectName);
		this.readProjectFile();
		this.createDB();
	}

	private void prepareDBConfiguration(String projectName) {
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = workspace.getRoot();
		IPath location = root.getLocation();
		String sPath = location.toOSString();
		sPath += File.separator + projectName + File.separator + "DataBase";
		System.out.printf("Path to Database: %s \n", sPath);
		this.PATHTODB = sPath;
		deleteAllFiles(new File(this.PATHTODB));

		Map<String, String> config = new HashMap<String, String>();
		config.put(Config.NODE_KEYS_INDEXABLE, "label");

		config.put(Config.RELATIONSHIP_KEYS_INDEXABLE,
				VDFRelTypes.relTypesToString());
		config.put(Config.NODE_AUTO_INDEXING, "true");
		config.put(Config.RELATIONSHIP_AUTO_INDEXING, "true");
		config.put(Config.ALLOW_STORE_UPGRADE, Boolean.toString(true));

		this.graphDb = new EmbeddedGraphDatabase(this.PATHTODB, config);
		// Activator.getDefault().getGraphDbServiceManager().setGraphServiceMode(this.graphDb);
		registerShutdownHook(this.graphDb);
	}

	private void readProjectFile() {
		Bundle bundle = Activator.getDefault().getBundle();
		Path path = new Path(File.separator
				+ "XML-Files" + File.separator + "projectSetup.xml"); //$NON-NLS-1$
		URL url = FileLocator.find(bundle, path, Collections.EMPTY_MAP);
		URL fileUrl = null;
		try {
			fileUrl = FileLocator.toFileURL(url);
		} catch (IOException e) {
			// Will happen if the file cannot be read for some reason
			e.printStackTrace();
		}

		this.projectFile = new File(fileUrl.getPath());
	}

	public void createDB() {

		Transaction tx = graphDb.beginTx();
		try {
			// create root nodes
			rootNode = graphDb.createNode();
			rootNode.setProperty("label", "Vertical Domain Facilities");

			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			if (this.projectFile.exists()) {
				Document doc = db.parse(this.projectFile);
				Element docEle = doc.getDocumentElement();

				// Print root element of the document
				System.out.println("Root element of the document: "
						+ docEle.getNodeName());

				NodeList nList = docEle.getChildNodes();

				this.loopXMLFile(nList, rootNode);

				tx.success();
				System.out.print("Done!");

			}

		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			tx.finish();
			graphDb.shutdown();
		}

	}

	private Node loopXMLFile(NodeList nList, Node parent) {

		Node parentTemp = null;

		for (int i = 0; i < nList.getLength(); i++) {

			org.w3c.dom.Node nodeXML = nList.item(i);

			if (nodeXML.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {

				System.out.println("\n =====================");

				Element e = (Element) nodeXML;
				if (!e.hasChildNodes()) {
					this.createVDFNode(e, parent);
				} else if (e.hasAttribute("relationshipType")) {
					if (e.hasAttribute("type")) {
						this.createVDFFileNode(e, parent);
					} else {
						parentTemp = this.createVDFNode(e, parent);
						loopXMLFile(e.getChildNodes(), parentTemp);
					}

				} else {
					loopXMLFile(e.getChildNodes(), parent);
				}

			}
		}

		return parentTemp;

	}

	private Node createVDFFileNode(Element e, Node relationshipNode) {

		ReadableIndex<Node> autoNodeIndex = graphDb.index()
				.getNodeAutoIndexer().getAutoIndex();

		NodeList nList = e.getElementsByTagName("relationship");

		System.out.print("\n Element name: " + e.getAttribute("name"));
		Node n = graphDb.createNode();
		n.setProperty("label", e.getAttribute("name"));
		relationshipNode.createRelationshipTo(n,
				VDFRelTypes.valueOf(e.getAttribute("relationshipType")));

		for (int i = 0; i < nList.getLength(); i++) {
			String relationNodeName = nList.item(i).getFirstChild()
					.getNodeValue().replaceAll("[\n\t]", "");
			String relationshipType = nList.item(i).getAttributes().item(1)
					.getNodeValue().replaceAll("[\n\t]", "");

			Node nodeTemp = autoNodeIndex.get("label", relationNodeName)
					.getSingle();
			n.createRelationshipTo(nodeTemp,
					VDFRelTypes.valueOf(relationshipType));
		}
		return n;
	}

	private Node createVDFNode(Element e, Node relationshipNode) {

		System.out.print("\n Element name: " + e.getAttribute("name"));
		
		Node nodeTemp = graphDb.index()
		.getNodeAutoIndexer().getAutoIndex()
		.get("label", e.getAttribute("name")).getSingle();
		if(nodeTemp == null){
			nodeTemp = graphDb.createNode();
			nodeTemp.setProperty("label", e.getAttribute("name"));
			if(e.hasAttribute("version")){
				nodeTemp.setProperty("version", e.getAttribute("version"));
			}
			if(e.hasAttribute("description")){
				nodeTemp.setProperty("description", e.getAttribute("description"));
			}
			
			relationshipNode.createRelationshipTo(nodeTemp,
					VDFRelTypes.valueOf(e.getAttribute("relationshipType")));		
		}
		else {
			relationshipNode.createRelationshipTo(nodeTemp,
					VDFRelTypes.valueOf(e.getAttribute("relationshipType")));
		}

		return nodeTemp;
	}

	public static boolean deleteAllFiles(File dir) {
		if (!dir.exists()) {
			return true;
		}
		boolean res = true;
		if (dir.isDirectory()) {
			File[] files = dir.listFiles();
			for (int i = 0; i < files.length; i++) {

				res &= deleteAllFiles(files[i]);
			}
			// res = dir.delete();//Delete dir itself
		} else {
			if (dir.getName() != "dummy.file")
				res = dir.delete();
		}
		return res;
	}

	private static void registerShutdownHook(final GraphDatabaseService graphDb) {
		// Registers a shutdown hook for the Neo4j instance so that it
		// shuts down nicely when the VM exits (even if you "Ctrl-C" the
		// running example before it's completed)
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
			public void run() {
				graphDb.shutdown();
			}
		});
	}

}
